package structs

import (
	"fmt"
	"qingbing/xzutils/strs"
	"qingbing/xzutils/utils"
	"reflect"
)

// 获取结构体名称
func GetName(value any) string {
	rt := utils.GetReflectType(value)
	if rt.Kind() != reflect.Struct {
		panic(fmt.Sprintf("%v is not a struct", value))
	}
	return rt.Name()
}

// 为结构体的指定属性赋值
func SetFieldValue(binding interface{}, name string, value interface{}) {
	bTyp := utils.GetReflectType(binding)
	if bTyp.Kind() != reflect.Struct {
		return
	}
	bVal := utils.GetReflectValue(binding)

	val := bVal.FieldByName(name)
	if val.IsValid() {
		field, _ := bTyp.FieldByName(name)
		key := field.Tag.Get("set")
		if key == "-" {
			return
		}
		// 设置字段值
		if val.CanSet() && val.Kind() == reflect.ValueOf(value).Kind() {
			val.Set(reflect.ValueOf(value))
			return
		}
	}

	num := bVal.NumField()
	for i := 0; i < num; i++ {
		field := bTyp.Field(i)
		key := field.Tag.Get("set")
		if key == "-" {
			continue
		}
		if !strs.IsFirstUpper(bTyp.Field(i).Name) {
			continue
		}
		if bTyp.Field(i).Type.Kind() == reflect.Struct || bTyp.Field(i).Type.Kind() == reflect.Ptr {
			SetFieldValue(bVal.Field(i).Interface(), name, value)
		}
	}
}

// 将 结构体 value 的值赋值给 结构体 binding
func ToStruct(binding interface{}, value interface{}) error {
	bVal := reflect.ValueOf(binding)
	if bVal.Kind() != reflect.Ptr || bVal.Elem().Kind() != reflect.Struct {
		return fmt.Errorf("binding is not valid pointer") // 程序员代码错误
	}
	bVal = bVal.Elem()

	vVal := reflect.ValueOf(value)
	vType := reflect.TypeOf(value)
	if vType.Kind() == reflect.Ptr {
		vVal = vVal.Elem()
		vType = vType.Elem()
	}
	if vType.Kind() != reflect.Struct {
		return fmt.Errorf("value is not valid struct") // 程序员代码错误
	}

	num := vVal.NumField()
	for i := 0; i < num; i++ {
		name := vType.Field(i).Name
		if ok := bVal.FieldByName(name).IsValid(); ok { // 判断变量是否有效
			vField := reflect.ValueOf(vVal.Field(i).Interface())
			if vField.IsValid() && bVal.FieldByName(name).CanSet() && bVal.FieldByName(name).Kind() == vVal.Field(i).Kind() {
				bVal.FieldByName(name).Set(vField)
			}
		} else if vVal.Field(i).Kind() == reflect.Struct || (vVal.Field(i).Kind() == reflect.Ptr && vVal.Field(i).Elem().Kind() == reflect.Struct) {
			ToStruct(binding, vVal.Field(i).Interface())
		}
	}
	return nil
}
